
import readline

import json
import sys, os, subprocess

from crypt import Crypt
from helper import edit_help
from fzf import fzf
from config import config
from passgen import PassGen
from cache import Cache


class Storage:
    def __init__(self, storage_name=None):
        self.crypt = Crypt()
        self.path = f"{config.storage_dir}/{storage_name}"
        self.raw_data = None

        if not storage_name:
            self.path = f"{config.storage_dir}/{select_storage()}"

    def exit_(self, message=None):
        subprocess.run(["/usr/bin/clear"])

        if message:
            print(f"Storage: {message}")
            sys.exit()

        if self.check_storage_changed():
            print(f"    Storage: Changes not saved.")

            if str(input("Exit Continue? y/n: ")).lower() == "y":
                sys.exit()
        else:
            sys.exit()

    def check_storage_changed(self):
        if self.old_raw_data != self.raw_data:
            return True
        else:
            return False

    def _key_to_lowercase(self):
        _temp_dict = {}
        for k in self.raw_data.keys():
            _temp_dict[k.lower()] = self.raw_data[k]

        self.raw_data = _temp_dict

    def open(self):
        try:
            self.raw_data = json.loads( self.crypt.decrypt_file(self.path) )
            self._key_to_lowercase()
        except json.decoder.JSONDecodeError:
            print(f"Storage: error load file `{self.path}`, json decode error.", file=sys.stderr)
            sys.exit(1)

        except FileNotFoundError as error:
            print(f"Storage: error load file `{self.path}`, not found.", file=sys.stderr)
            sys.exit(2)

    def __getitem__(self, key):
        if not self.raw_data:
            self.open()

        return self.raw_data.get(str(key), None)

    def create(self):
        if os.path.isfile(self.path):
            print(f"File `{self.path}` exists. Replace? y/n")

            if input().lower() == "y":
                self.crypt.load_data("{}".encode())
                self.crypt.encrypt_file(self.path)
            else:
                sys.exit()
        else:
            self.crypt.load_data("{}".encode())
            self.crypt.encrypt_file(self.path)

        self.edit()

    def save(self):
        self.crypt.load_data(json.dumps(self.raw_data, indent=4, sort_keys=True, ensure_ascii=False).encode())
        self.crypt.encrypt_file(self.path)

        self.old_raw_data = self.raw_data.copy()

    def edit(self):
        self.open()

        self.old_raw_data = self.raw_data.copy()

        subprocess.run(["/usr/bin/clear"])

        storage_name = self.path.split('/')[-1]

        while True:
            title = f"  [{storage_name}{'*' if self.check_storage_changed() else ''}]"
            try:
                arg, opt, val, *exval = (input(f"{title}: ").split(" ") + [None] * 3)
            except KeyboardInterrupt:
                sys.exit()

            if opt:
                opt = opt.lower()

            match arg.lower():
                case "help" | "h":
                    edit_help()

                case "exit" | "q":
                    self.exit_()

                case "clear" | "c":
                    subprocess.run(["/usr/bin/clear"])

                case "save" | "w":
                    self.save()

                case "wq":
                    self.save()
                    self.exit_()

                case "rename":
                    old_key = opt
                    new_key = val
                    value = self.raw_data[str(opt)]

                    if self.raw_data.get(str(new_key), None):
                        print(f"    Key: `{new_key}` is exists.\n")
                        continue

                    del self.raw_data[str(old_key)]

                    self.raw_data[str(new_key)] = str(value)

                    print(f"    Renamed: `{old_key}` to `{new_key}`\n")

                case "ls" | "l":
                    if not opt:
                        for key in self.raw_data.keys():
                            print(f"    {key}: {self.raw_data.get(key, None)}")
                    else:
                        print(f"    {str(opt)}: {self.raw_data.get(str(opt), None)}")

                    print("\n")

                case "del" | "rm":
                    print(f"    Remove: `{str(opt)}: {self.raw_data[str(opt)]}`\n")
                    del self.raw_data[str(opt)]

                case "set":
                    if "random" in str(val).lower():
                        try:
                            try:
                                lens = int(exval[0])
                                passgen = PassGen( lens=lens )
                            except:
                                if exval[1]:
                                    lens = int(exval[1])
                                else:
                                    lens = None

                                passgen = PassGen( exval[0], lens=lens)

                            val = passgen.gen()
                        except TypeError as error:
                            print(f"    Error: `Incorrect data for generation password`.\n    {error}\n")
                            continue

                        except ValueError as error:
                            print(f"    Error: `Incorrect data for generation password`.\n    Example: A-Za-z0-9\n    {error}\n")
                            continue

                        except Exception as error:
                            print(f"    Storage: `Unknown error`\n`{error}`.", file=sys.stderr)
                            sys.exit(1)

                    self.raw_data[str(opt)] = str(val)
                    print(f"    Added: `{str(opt)}: {self.raw_data[str(opt)]}`\n")


def select_storage():
    cache = Cache()

    list_storage = sorted( os.listdir(config.storage_dir) )

    if cache["last_storage"]:
        try:
            list_storage.remove( cache["last_storage"] )
            list_storage.append( cache["last_storage"] )
        except:
            ...

    fzf_ = fzf()

    choice = fzf_.choice( list_storage[::-1] )

    if not choice:
        print(f"Storage is empty")
        sys.exit()

    if not os.path.isfile(f"{config.storage_dir}/{choice}"):
        exit_(f"Not storage `{choice}`")

    cache["last_storage"] = choice
    cache.save()

    return choice

